#pragma once

template<typename DATA_TYPE>
class Dynamic_Array{
private:
	unsigned int m_sizeUsed, m_sizeCapacity;
	unsigned int m_sizeIncrement;

	DATA_TYPE *m_data;

	void increaseSize(){
		if((m_sizeUsed % m_sizeIncrement) == 0 || m_sizeUsed == m_sizeCapacity){
			int t_sizeOldUsed = m_sizeUsed;
			int t_sizeNewCapacity = m_sizeUsed + m_sizeIncrement;;
			DATA_TYPE *t_newData = new DATA_TYPE[t_sizeNewCapacity];
			if(m_data){
				for(unsigned int i = 0; i < m_sizeUsed; ++i){
					t_newData[i] = m_data[i];
				}
				clear();
			}
			m_data = t_newData;
			m_sizeUsed = t_sizeOldUsed;
			m_sizeCapacity = t_sizeNewCapacity;
		}
	};

	bool passBoundCheck(unsigned int a_index){
		if((a_index > (m_sizeUsed - 1)) || (a_index < 0) || (m_sizeUsed == 0)){
			return false;
		}
		return true;
	};

	bool passSizeCheck(){
		if((m_sizeUsed < 0) || (m_sizeUsed == 0)){
			clear();
			return false;
		}
		return true;
	};

	void decreaseSize(){
		if(m_sizeUsed < (m_sizeCapacity-m_sizeIncrement)){
			int t_sizeOldUsed = m_sizeUsed;
			int t_sizeNewCapacity = m_sizeUsed - 1;
			if(t_sizeNewCapacity == 0){t_sizeNewCapacity = 1;}
			DATA_TYPE *t_newData = new DATA_TYPE[t_sizeNewCapacity];
			if(m_data){
				for(int i = 0; i < t_sizeNewCapacity; ++i){
					t_newData[i] = m_data[i];
				}
				clear();
			}
			m_data = t_newData;
			m_sizeUsed = t_sizeOldUsed;
			m_sizeCapacity = t_sizeNewCapacity;
		}
	};

	void clear(DATA_TYPE *&a_DATA_TYPE){
		if(m_data != 0){
			delete [] m_data;
			m_data = 0;
		}
	};

public:
	Dynamic_Array(){
		m_sizeUsed = 0;
		m_sizeCapacity = m_sizeIncrement = 4;
		m_data = 0;
		clear();
	};
	~Dynamic_Array(){
		clear();
	};

	int getSizeUsed(){
		return m_sizeUsed;
	};
	int getSizeCapacity(){
		return m_sizeCapacity;
	};
	void setSizeIncrement(unsigned int a_sizeIncrement){
		m_sizeIncrement = a_sizeIncrement;
	};

	DATA_TYPE getIndex(unsigned int a_index){
		DATA_TYPE tempDataType = 0;
		if(!passBoundCheck(a_index)){return tempDataType;}

		if(m_data){
			return m_data[a_index];
		}

		return tempDataType;
	};

	void changeIndex(unsigned int a_index, DATA_TYPE a_data){
		if(!passBoundCheck(a_index)){return;}

		m_data[a_index] = a_data;
	};

	bool insertToIndex(unsigned int a_index, DATA_TYPE a_data){
		if(!passBoundCheck(a_index)){return false;}
		if(a_index == 0){
			return false;
		}

		increaseSize();

		for(unsigned int from = m_sizeUsed - 1; from >= a_index; from--){
			m_data[from + 1] = m_data[from];
		}
		m_data[a_index] = a_data;

		m_sizeUsed++;
		return true;
	};

	void removeFromIndex(unsigned int a_index){
		if(!passBoundCheck(a_index)){return;}
		if(!passSizeCheck()){return;}

		m_data[a_index] = 0;
		for(unsigned int from = a_index; from <= m_sizeUsed - 1; from++){
			m_data[from] = m_data[from + 1];
		}

		decreaseSize();

		m_sizeUsed--;

		if(!passSizeCheck()){return;}
	};

	void insertToEnd(DATA_TYPE a_data){
		increaseSize();

		m_data[m_sizeUsed] = a_data;

		m_sizeUsed++;
	};

	void removeFromEnd(){
		if(!passSizeCheck()){return;}

		m_data[m_sizeUsed-1] = 0;
		decreaseSize();

		m_sizeUsed--;

		if(!passSizeCheck()){return;}
	};

	void clear(){
		clear(m_data);
		m_sizeUsed = m_sizeCapacity = 0;
	};

};

template<typename DATA_TYPE>
class Dynamic_Array<DATA_TYPE *>{
private:
	unsigned int m_sizeUsed, m_sizeCapacity;
	unsigned int m_sizeIncrement;

	DATA_TYPE **m_data;

	void increaseSize(){
		if((m_sizeUsed % m_sizeIncrement) == 0 || m_sizeUsed == m_sizeCapacity){
			int t_sizeOldUsed = m_sizeUsed;
			int t_sizeNewCapacity = m_sizeUsed + m_sizeIncrement;;
			DATA_TYPE **t_newData = new DATA_TYPE*[t_sizeNewCapacity];
			if(m_data){
				for(unsigned int i = 0; i < m_sizeUsed; ++i){
					t_newData[i] = m_data[i];
				}
				//clear();
				delete [] m_data;
				m_data = 0;
			}
			m_data = t_newData;
			m_sizeUsed = t_sizeOldUsed;
			m_sizeCapacity = t_sizeNewCapacity;
		}
	};

	bool passBoundCheck(unsigned int a_index){
		if((a_index > (m_sizeUsed - 1)) || (a_index < 0) || (m_sizeUsed == 0)){
			return false;
		}
		return true;
	};

	bool passSizeCheck(){
		if((m_sizeUsed < 0) || (m_sizeUsed == 0)){
			clearAll();
			return false;
		}
		return true;
	};

	void decreaseSize(){
		if(m_sizeUsed < (m_sizeCapacity-m_sizeIncrement)){
			int t_sizeOldUsed = m_sizeUsed;
			int t_sizeNewCapacity = m_sizeUsed - 1;
			if(t_sizeNewCapacity == 0){t_sizeNewCapacity = 1;}
			DATA_TYPE **t_newData = new DATA_TYPE*[t_sizeNewCapacity];
			if(m_data){
				for(int i = 0; i < t_sizeNewCapacity; ++i){
					t_newData[i] = m_data[i];
				}
				delete [] m_data;
				m_data = 0;
			}
			m_data = t_newData;
			m_sizeUsed = t_sizeOldUsed;
			m_sizeCapacity = t_sizeNewCapacity;
		}
	};

public:
	Dynamic_Array(){
		m_sizeUsed = 0;
		m_sizeCapacity = m_sizeIncrement = 4;
		m_data = 0;
		clearAll();
	};
	~Dynamic_Array(){
		clearAll();
	};

	int getSizeUsed(){
		return m_sizeUsed;
	};
	int getSizeCapacity(){
		return m_sizeCapacity;
	};
	void setSizeIncrement(unsigned int a_sizeIncrement){
		m_sizeIncrement = a_sizeIncrement;
	};

	DATA_TYPE *getIndex(unsigned int a_index){
		DATA_TYPE *tempDataType = 0;
		if(!passBoundCheck(a_index)){return tempDataType;}

		if(m_data){
			return m_data[a_index];
		}

		return tempDataType;
	};

	void changeIndex(unsigned int a_index, DATA_TYPE *a_data){
		if(!passBoundCheck(a_index)){return;}

		m_data[a_index] = a_data;
	};

	bool insertToIndex(unsigned int a_index, DATA_TYPE *a_data){
		if(!passBoundCheck(a_index)){return false;}
		if(a_index == 0){
			return false;
		}

		increaseSize();

		for(unsigned int from = m_sizeUsed - 1; from >= a_index; from--){
			m_data[from + 1] = m_data[from];
		}
		m_data[a_index] = a_data;

		m_sizeUsed++;
		return true;
	};

	void removeFromIndex(unsigned int a_index){
		if(!passBoundCheck(a_index)){return;}
		if(!passSizeCheck()){return;}

		delete m_data[a_index];
		m_data[a_index] = 0;
		for(unsigned int from = a_index; from <= m_sizeUsed - 1; from++){
			m_data[from] = m_data[from + 1];
		}

		decreaseSize();

		m_sizeUsed--;

		if(!passSizeCheck()){return;}
	};

	void insertToEnd(DATA_TYPE *a_data){
		increaseSize();

		m_data[m_sizeUsed] = a_data;

		m_sizeUsed++;
	};

	void removeFromEnd(){
		if(!passSizeCheck()){return;}

		delete m_data[m_sizeUsed-1];
		m_data[m_sizeUsed-1] = 0;
		decreaseSize();
		m_sizeUsed--;

		if(!passSizeCheck()){return;}
	};

	void clearAll(){
		if(m_data){
			for(unsigned int i = 0; i < m_sizeUsed; i++){
				if(m_data[i] != 0){
					delete m_data[i];
					m_data[i] = 0;
				}
			}
			delete m_data;
			m_data = 0;
		}
		m_sizeUsed = m_sizeCapacity = 0;
	};

	void clearPtr(){
		if(m_data){
			delete m_data;
			m_data = 0;
		}
	};

};